<%@ page import="java.io.*, java.net.*, java.util.*" %>
<%@ page import="java.sql.*" %>
<%@ page import="org.w3c.dom.*, javax.xml.parsers.*" %>
<%@ page import="org.json.JSONObject" %>
<%@ page import="org.json.JSONArray" %>
<%@ page import="org.json.JSONException" %>
<%@ page import="sun.misc.BASE64Decoder" %>
<%@ page import="sun.misc.BASE64Encoder" %>
<%@ page import="javax.crypto.spec.SecretKeySpec" %>
<%@ page import="javax.crypto.Mac" %>
<%@ page import="java.io.UnsupportedEncodingException" %>
<%@ page import="java.security.InvalidKeyException" %>
<%@ page import="java.security.NoSuchAlgorithmException" %>


<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

<% // scriplets
    System.out.println("############### compt1: "+compteur+" comp2: "+compteur2);
    System.out.println("request");
     printRequestParams(request);

     System.out.println("signed_request");
     // Le parametre "signed_request" est utilise pour partager l'info entre l'application et facebook
    // String sigreq = request.getParameter("signed_request");
     //Test avec cle fournie
    // String sigreq = "qmCgD3B4xeVE3lps2uG8YCdVJV7yTTkw5NfWhm1vKkE.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZF9hdCI6MTMzMDA4OTk1OCwidXNlciI6eyJjb3VudHJ5IjoiZnIiLCJsb2NhbGUiOiJmcl9GUiIsImFnZSI6eyJtaW4iOjIxfX19";
     String sigreq = "7ixYn21ckLbzgBrHB-i1Cr6giRVMTTXeHG65xW9jyAQ.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZF9hdCI6MTMzMDA5MDA5OSwidXNlciI6eyJjb3VudHJ5IjoiZnIiLCJsb2NhbGUiOiJmcl9GUiIsImFnZSI6eyJtaW4iOjIxfX19";
    if (sigreq == null) {
        out.write(errMessage);
        return;
    }

     System.out.println("parsing");

      JSONObject data = parseSignedRequest(sigreq, appSecret);
            if (data != null) {
                 System.out.println("part1 - data: " + data.toString());
		 System.out.println("- data: oauth_token "+ data.has("oauth_token"));
		// on entre dans ce if qu'une fois que le user a autorise notre application
		// car c'est seulement a ce moment qu'on obtient le oauth_token et le user_id
                if (data.has("oauth_token") && data.has("user_id")) {
		 //recuperer l'userID et le token contenu dans l'objet JSON
                    String fb_id = data.getString("user_id");
                    String oauth_token = data.getString("oauth_token");
                    String ipAddress = request.getRemoteAddr();

                    if (!checkUserPermissions(oauth_token)) {
                        //si l'utilisateur n'a pas autorisé notre app
                        authorize(out);
                        System.out.println("OUT => "+out);
                        return;
                    }

                    System.out.println("TEST1");
                    if (!login(oauth_token, fb_id, ipAddress)) {
                        System.out.println("PB!!!!!!");
                        out.write(errMessage);
                        out.write(loginError);
                        return;
                    }
                    System.out.println("TEST2");
                    //construction du fichier "fb_jsp.log"
                    //logger.println("Login success: " + fb_id);
                    //logger.println("Session key: " + sessionKey);
                   // logger.flush();
                    System.out.println("Login success: " + fb_id);
                    System.out.println("Session key: " + sessionKey);

                } else {
                    // si le user n'a pas encore autorise notre application on entre dans le else
                    authorize(out);
                    compteur2++;
                    return;
                }
            } else {
                out.write(errMessage);
                compteur++;
                return;
            }



%>

<%!
    public int compteur = 0;
    public int compteur2 = 0;
    public final String errMessage = "Server error! Sorry.";
    public String appId = "243622595719459";
    public String appSecret = "bcb2e1e8e9ba4c0d976eb807548a66a4";
    public String appUrl = "http://apps.facebook.com/insa_power/";
    public String permissions[] = {"publish_stream", "offline_access","friends_hometown"};
    public String sessionKey = "";
    public String locale = "";
    public String name = "";
    public String sqlConnectionURL = "";
    public String sqlLogin = "";
    public String sqlPass = "";
    public String loginError = "";
    public JSONObject jsonObject = new JSONObject();
    public JSONArray jsonArrayFriends = new JSONArray();
    private static final String ALGORITHM_HMAC_SHA_256 = "HmacSHA256";

/**********************************************
** Affiche les parametres et valeurs         **
** des requetes effectuees  					   **
** Pour construire le fichier "fb_jsp.log"   **
**********************************************/
    public void printRequestParams(HttpServletRequest request) {

        System.out.println("+++++++++++++");

        System.out.println(request.getRequestURL().toString());

        //fonction qui enumere les noms des parametres demandes via la requete
        Enumeration paramNames = request.getParameterNames();
        System.out.println("-- "+paramNames.hasMoreElements());

	//while => parcorus de la liste des parametres utilises
	//hasMoreElements => teste si cette enumeration contient encore des elements
        while (paramNames.hasMoreElements()) {
            String paramName = (String) paramNames.nextElement();

            System.out.println("paramName - " + paramName);


            //affiche les valeurs du parametre
            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues.length == 1) {
                String paramValue = paramValues[0];
                if (paramValue.length() == 0) {
                    System.out.println("no value");

                } else {
                    System.out.println("***"+paramValue);

                }
            } else {
                for (int i = 0; i < paramValues.length; i++) {
                    System.out.println("###"+paramValues[i] );

                }
            }
        }

    }

    /*************************************
** Fonction qui permet de recuperer **
** la signature HMAC SHA-256 et 	   **
** un objet JSON encode base64url   **
**************************************/
    public JSONObject parseSignedRequest(String signedRequest, String secret) {
        System.out.println("Fct parseSignedRequest =>");

        String[] list = signedRequest.split("\\.");
        if (list.length >= 2) {
            String encodedSig = list[0];
            String payload = list[1];

            try {
		// L'object JSON contient les champs: user, algorithm, user_id, ...
                JSONObject data = new JSONObject(new String(fbDecodeBase64(payload)));
                System.out.println("jsonobject data => "+data);

		 //on verifie si l'algo utilisee est bien celle utilisee par fb
                if (!"HMAC-SHA256".equalsIgnoreCase(data.getString("algorithm"))) {
                    System.out.println("Unknown algorithm. Expected HMAC-SHA256, signedRequest={}" + signedRequest);

                    return null;
                }

		 //si la signature est correcte alors on renvoie les donnees contenues dans l'objet JSON
                if (verifySignedRequest(payload, encodedSig, secret)) {
                    System.out.println("Signed Requet verfied! signedRequest={}" + signedRequest);

                    return data;

                } else {
                    System.out.println("Invalid Signed Requet! signedRequest={}"+signedRequest);
                }

            } catch (IOException e) {
                System.out.println("Error Decoding Base64, signedRequest="+signedRequest+" -- "+e);

            } catch (JSONException e) {

                System.out.println("Invalid Json Data, signedRequest="+signedRequest+" -- "+e);
            }

        }
        return null;
    }

/********************************************
** Fonction qui verifie la signed_request	 **
********************************************/

    
    public static boolean verifySignedRequest(String payload, String encodedSig, String secret) {
        System.out.println("Fct verifySignedRequest =>");
         try {
            //encode le string avec le charset UTF-8
            byte[] keyBytes = secret.getBytes("UTF-8");

            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ALGORITHM_HMAC_SHA_256);
            Mac mac = Mac.getInstance(ALGORITHM_HMAC_SHA_256);
            mac.init(secretKeySpec);
            byte[] macBytes = mac.doFinal(payload.getBytes());

            String encodedMacBytes = fbEncodeBase64(macBytes);
            return encodedSig.equals(encodedMacBytes);

        } catch (NoSuchAlgorithmException e) {
            System.out.println("Unable to get HmacsSHA256 instance "+ e);

        } catch (InvalidKeyException e) {
            System.out.println("Invalid Key for HmacsSHA256  "+e);

        } catch (UnsupportedEncodingException e) {
            System.out.println("Error Getting Bytes Array, secret= "+secret+" -- "+e);

        }
        return false;
    }

/*************************************
** Fonction qui encode l'objet byte   **
** en base64url            	      **
**************************************/
    private static String fbEncodeBase64(byte[] bytes) {
        System.out.println("Fct fbEncodeBase64 =>");

        String encodedMacBytes = new BASE64Encoder().encode(bytes);
        encodedMacBytes = encodedMacBytes.replaceAll("\\+", "-");
        encodedMacBytes = encodedMacBytes.replaceAll("/", "_");
        if (encodedMacBytes.endsWith("=")) {
            encodedMacBytes = encodedMacBytes.substring(0, encodedMacBytes.length() - 1);
        }
        return encodedMacBytes;
    }

/*************************************
** Fonction qui decode l'objet JSON	**
** qui est encode en base64url   	**
*************************************/
    private static byte[] fbDecodeBase64(String encoded) throws IOException {
        System.out.println("Fct fbDecodeBase64 => ");

        String _encoded = encoded.replaceAll("-", "+");
        _encoded = _encoded.replaceAll("_", "/");
        return new BASE64Decoder().decodeBuffer(_encoded);
    }



/************************************************
** Focntion qui affiche l'URL d'autorisation  **
*************************************************/
    public void authorize(JspWriter out) {
        System.out.println("Fct authorize => ");

        String url = getAuthorizeUrl();
        System.out.println("URL 1=> "+url);

        try {
            out.write("<script type='text/javascript'>top.location.href = '" + url + "';</script>");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

/*******************************
** 	**
** 	**
*********************************/
    public boolean checkUserPermissions(String oauth_token) {
        System.out.println("Fct checkUserPermissions => ");

        //use Old REST API to check user permissions
        try {
            String url;
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            //Using this class(DocumentBuilder), an application programmer can obtain a Document from XML
            DocumentBuilder db = dbf.newDocumentBuilder();
            for (String str : permissions) {
                url = "https://api.facebook.com/method/users.hasapppermission?api_key=" + appId + "&ext_perm=" + str + "&access_token=" + oauth_token;
                InputStream stream = sendGETStream(url);
                Document doc = db.parse(stream);
                NodeList nlist = doc.getElementsByTagName("users_hasapppermission_response");
                //Si la reponse est 1 cela veut dire que l'user a autorise notre app
                Boolean value = Boolean.parseBoolean(nlist.item(0).getTextContent().equals("0") ? "false" : "true");
                //logger.println("ext_perm = " + str + " - " + value);
                if (!value) {
                    return false;
                }
            }
        } catch (Exception e) {
            //logger.println(e.getMessage());
            return false;
        }

        return true;
    }

/****************************************************************
** Fonction qui "prepare" l'url pour la demande d'autorisation **
** d'acces aux donnees de l'user											**
*****************************************************************/
    public String getAuthorizeUrl() {
        System.out.println("Fct getAuthorizeUrl => ");
   //idem? https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_CANVAS_PAGE

        String result = "https://graph.facebook.com/oauth/authorize?client_id=" + appId + "&redirect_uri=" + appUrl;

/*you must request specific permissions from the user.
This is accomplished by adding a scope parameter to the OAuth Dialog request followed by comma separated list of the required permissions*/
        if (permissions.length > 0) {
            result = result.concat("&scope=");
        }
		  //pour chaque permission qu'on desire demander a l'user via notre application, il faudra l'ajouter dans l'url de demande d'autorisation
		  //dans l'url chaque permission est separee d'une ","
        for (String str : permissions) {
            result = result.concat(str + ",");
        }
        return result;
        //return (permissions.length > 0) ? result.substring(0, result.length() - 1) : result;
    }

/*******************************
** **
** **
*********************************/
    public boolean login(String oauth_token, String fb_id, String ipAddress) {
        System.out.println("Fct login => ");

        try {
            System.out.println("try login");
            //jsonObject.put("oauth_token", oauth_token);
            //jsonObject.put("fb_id", fb_id);
            //jsonObject.put("ip", ipAddress);

            //get profile data
            String urlStr = "https://graph.facebook.com/me?access_token=" + oauth_token;
            String response = sendGET(urlStr);
            System.out.println("url response => "+response);
            //logger.println("me? response = " + response);
            //logger.flush();
            if (response != null) {

                JSONObject profile = new JSONObject(response);
                if (profile.has("error")) {
                    loginError = profile.getJSONObject("error").toString();
                    return false;
                }
                //get user's picture
                //urlStr = "https://graph.facebook.com/me/picture?access_token=" + oauth_token;
                //String pic = getPicture(urlStr);
               // profile.put("picture", pic);

                //get user's friends list
                urlStr = "https://graph.facebook.com/me/friends?fields=id,name,birthday,hometown&access_token=" + oauth_token;
                JSONArray friends = new JSONObject(sendGET(urlStr)).getJSONArray("data");
                //decode friend's array and find all hometown's
               /*
                for (int i=0; i<friends.length(); i++){
                JSONObject friend = friends.getJSONObject(i);
                urlStr = "https://graph.facebook.com/"+ friend.getInt("id") +"?access_token=" + oauth_token;
                System.out.println(urlStr);
                JSONObject pr = new JSONObject(sendGET(urlStr)).getJSONObject("data");
                System.out.println(pr.toString());
                friends.getJSONObject(i).put("hometown",pr.getString("hometown"));
                }*/

                //profile.put("friends", friends.toString());
                //logger.println("friends = " + friends.toString());
                jsonArrayFriends = friends;
		//put permet d'ecrire {"profile": les val de la var profile}
                jsonObject.put("profile", profile);
            } else {
                return false;
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }

        return true;
    }

    /*******************************
** 	**
** 	**
*********************************/
    public String streamToStr(InputStream in) throws IOException {
        System.out.println("Fct streamToStr => ");

        StringBuffer out = new StringBuffer();
        byte[] b = new byte[4096];
        for (int n; (n = in.read(b)) != -1;) {
            out.append(new String(b, 0, n));
        }
        return out.toString();
    }

/*******************************
** 	**
** 	**
*********************************/
    public InputStream sendGETStream(String urlStr) {
        System.out.println("Fct sendGETStream => ");

        try {
            URL url = new URL(urlStr);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.connect();
            InputStream result = urlConnection.getInputStream();
            //urlConnection.disconnect();
            return result;
        } catch (Exception e) {
            //logger.println(e.getMessage());
            //e.printStackTrace();
            return null;
        }
    }

/*******************************
** 	**
** 	**
*********************************/
    public String sendGET(String urlStr) {
        System.out.println("Fct sendGET => ");

        try {
            InputStream stream = sendGETStream(urlStr);
            return streamToStr(stream);
        } catch (Exception e) {
            //logger.println(e.getMessage());
            //e.printStackTrace();
            return null;
        }
    }


%>



<%@page language="java" contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">


<%--
    This file is an entry point for JavaServer Faces application.
--%>
<f:view>
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb='http://www.facebook.com/2008/fbml' lang="en" xml:lang="en">
    <head>
        <title>TP</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
        <style type="text/css">
            html { height: 100% }
            body { height: 100%; margin: 0px; padding: 0px }
        </style>
        <script src="http://connect.facebook.net/en_US/all.js"></script>

        <script type="text/javascript">
            var profile = <%= jsonObject%>;
            var friends = <%= jsonArrayFriends%>;

             function initialize() {
                displayFriends();
                window.fbAsyncInit = function() {
                    FB.Canvas.setAutoResize();
                }
             }

            function displayFriends() {
                var list="";
                target = document.getElementById("listeOfFriends");
                for (var i=0;i<friends.length; i++){
                    list += friends[i]['name'] + "<BR/>" ;
                }
                target.innerHTML = list;
            }

        </script>
    </head>

    <body onload="initialize()>
    <!-- la fonction initialize s'execute une fois que la page html est chargee -->
        <div id="fb-root"></div>
        <h1>Liste</h1>
        <div id="listeOfFriends"></div>

    </body>
</html>
</f:view>
